/*
 * Copyright (C) 2019 ByteDance Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bytedance.scene.ui;


import com.bytedance.scene.NavigationSceneUtility;
import com.bytedance.scene.ResourceTable;
import com.bytedance.scene.Scene;
import com.bytedance.scene.SceneDelegate;
import com.bytedance.scene.animation.AnimationInfo;
import com.bytedance.scene.animation.NavigationAnimationExecutor;
import com.bytedance.scene.interfaces.PushOptions;
import com.bytedance.scene.interfaces.PushResultCallback;
import com.bytedance.scene.navigation.NavigationScene;
import com.bytedance.scene.navigation.NavigationSceneGetter;
import com.bytedance.scene.navigation.NavigationSceneOptions;
import com.bytedance.scene.SingeProcessMessengerHandler;
import com.bytedance.scene.utlity.AnimatorUtility;
import com.bytedance.scene.utlity.CancellationSignal;
import com.bytedance.scene.utlity.NonNullPair;
import ohos.aafwk.ability.fraction.FractionAbility;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.IntentParams;
import ohos.aafwk.content.Operation;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.LayoutScatter;
import ohos.app.Context;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
 * Created by JiangQi on 9/3/18.
 *
 * Can not use singleTop here, because we need to determine whether the theme is the same,
 * and sInstance can achieve the same effect of the singleTop mode as well.
 *
 * Todo: Handle multiple startup
 */
public class SceneContainerAbility extends FractionAbility implements SceneNavigationContainer {
    private static final String EXTRA_CLASS_NAME = "class_name";
    private static final String EXTRA_THEME = "theme";
    private static final String EXTRA_ARGUMENTS = "arguments";

    public static Intent newIntent(Context context, int themeId, Class<? extends Scene> clazz, IntentParams bundle) {
        Intent intent = new Intent();
        Operation operation=new Intent.OperationBuilder()
                .withBundleName(context.getBundleName())
                .withAbilityName(SceneContainerAbility.class)
                .build();
        intent.setOperation(operation);
        intent.setParam(EXTRA_CLASS_NAME, clazz.getName());
        intent.setParam(EXTRA_THEME, themeId);
        intent.setParam(EXTRA_ARGUMENTS, bundle);
        return intent;
    }

    private static final Set<PushResultCallback> PUSH_RESULT_CALLBACK_SET = new HashSet<>();

    private static PushResultCallback mPushResultCallback;

    public static PushResultCallback getPushResultCallback() {
        return mPushResultCallback;
    }

    public static void setPushResultCallback(PushResultCallback mPushResultCallback) {
        SceneContainerAbility.mPushResultCallback = mPushResultCallback;
    }

    public static Intent newIntentForResult(Context context, int themeId, Class<? extends Scene> clazz, IntentParams bundle,
                                            final PushResultCallback pushResultCallback) {
        Intent intent = new Intent();
        Operation operation=new Intent.OperationBuilder()
                .withBundleName(context.getBundleName())
                .withAbilityName(SceneContainerAbility.class)
                .build();
        intent.setOperation(operation);
        intent.setParam(EXTRA_CLASS_NAME, clazz.getName());
        intent.setParam(EXTRA_THEME, themeId);
        intent.setParam(EXTRA_ARGUMENTS, bundle);
        PUSH_RESULT_CALLBACK_SET.add(pushResultCallback);
        setPushResultCallback(pushResultCallback);
//        final WeakReference<PushResultCallback> reference = new WeakReference<>(pushResultCallback);
//        SingeProcessMessengerHandler.put(intent, new SingeProcessMessengerHandler.Callback() {
//            @Override
//            public void onResult(Object result) {
//                PushResultCallback callback = reference.get();
//                if (callback != null) {
//                    callback.onResult(result);
//                    PUSH_RESULT_CALLBACK_SET.remove(callback);
//                }
//            }
//        });
        return intent;
    }


    public static final List<SceneContainerAbility> sInstance = new ArrayList<>();
    private SceneDelegate mDelegate;
    private boolean mIsVisible = false;
    
    public int mThemeResId = -1;

    @Override
    protected void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_navigation_scene);
        this.mIsVisible = true;

        int themeId = intent.getIntParam(EXTRA_THEME, -1);
        if (themeId != -1) {
            setTheme(themeId);
        }
        this.mThemeResId = themeId;

        sInstance.add(this);
        SingeProcessMessengerHandler singeProcessMessengerHandler = SingeProcessMessengerHandler.from(intent);
        if (singeProcessMessengerHandler != null) {
            this.mDelegate = NavigationSceneUtility.setupWithAbility(this, intent,
                    new NavigationSceneOptions(DelegateScene.class, null), false);
        } else {
            NonNullPair<? extends Class<? extends Scene>, IntentParams> pair = getSceneDataFromIntent(intent);
            this.mDelegate = NavigationSceneUtility.setupWithAbility(this, intent,
                    new NavigationSceneOptions(pair.first, pair.second), false);
        }
    }

    public static class DelegateScene extends Scene {

        @Override
        public Component onCreateView(LayoutScatter scatter, ComponentContainer container, Intent intent) {
            return new Component(requireAbility());
        }

        @Override
        public void onAbilityCreated(Intent i) {
            super.onAbilityCreated(i);
            Intent intent = requireAbility().getIntent();
            NonNullPair<? extends Class<? extends Scene>, IntentParams> pair = getSceneDataFromIntent(intent);
            NavigationSceneGetter.requireNavigationScene(this).push(pair.first,
                    pair.second, new PushOptions.Builder().setAnimation(new KeepAnimationExecutor())
                            .setPushResultCallback(new PushResultCallback() {
                                @Override
                                public void onResult(Object result) {
                                    SingeProcessMessengerHandler singeProcessMessengerHandler = SingeProcessMessengerHandler.from(requireAbility().getIntent());
                                    singeProcessMessengerHandler.sendResult(result);
                                    requireAbility().terminateAbility();
                                }
                            }).build());
        }

    }

    private static class KeepAnimationExecutor extends NavigationAnimationExecutor {
        @Override
        public boolean isSupport( Class<? extends Scene> from,  Class<? extends Scene> to) {
            return true;
        }

        @Override
        public void executePushChangeCancelable( AnimationInfo fromInfo,  AnimationInfo toInfo,  Runnable endAction,  CancellationSignal cancellationSignal) {
            endAction.run();
        }

        @Override
        public void executePopChangeCancelable( AnimationInfo fromInfo,  AnimationInfo toInfo,  Runnable endAction,  CancellationSignal cancellationSignal) {
            final Component fromView = fromInfo.mSceneView;
            final Component toView = toInfo.mSceneView;

            AnimatorUtility.resetViewStatus(fromView);
            AnimatorUtility.resetViewStatus(toView);

            fromView.setVisibility(Component.VISIBLE);

            mAnimationViewGroup.addComponent(fromView);

            endAction.run();
        }
    }

    
    private static NonNullPair<? extends Class<? extends Scene>, IntentParams> getSceneDataFromIntent(Intent intent) {
        Class<? extends Scene> clazz = null;
        try {
            clazz = (Class<? extends Scene>) Class.forName(intent.getStringParam(EXTRA_CLASS_NAME));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        IntentParams bundle = intent.getParam(EXTRA_ARGUMENTS);
        return NonNullPair.create(clazz, bundle);
    }

    @Override
    public void onBackPressed() {
        if (!this.mDelegate.onBackPressed()) {
            super.onBackPressed();
        }
    }

    @Override
    public NavigationScene getNavigationScene() {
        return this.mDelegate.getNavigationScene();
    }

    @Override
    public int getThemeId() {
        return this.mThemeResId;
    }



    @Override
    protected void onBackground() {
        super.onBackground();
        mIsVisible = false;
    }

    @Override
    protected void onStop() {
        super.onStop();
        sInstance.remove(this);
    }


    @Override
    public boolean isVisible() {
        return this.mIsVisible;
    }
}
